//=============================================================================
//	$Id: rational.cpp 117 2011-11-11 11:58:38Z sergey.kazenyuk $
//	$URL: http://spbau-cpp-2011.googlecode.com/svn/trunk/kazenyuk/hw02_rational/src/rational.cpp $
//=============================================================================

#include <cmath>
#include <cstdint>
#include <iostream>

template <typename Z>
Z greatest_common_divisor(Z x, Z y)
{
	if (y == 0)
	{
		return x;
	}
	return greatest_common_divisor(y, x % y);
}

template <typename Z>
Z least_common_multiple(Z x, Z y)
{
	return (std::abs(x)/greatest_common_divisor(x,y)) * std::abs(y);
}

template <typename Z = int32_t>
class Rational
{
public:
	Rational<Z>(Z numerator, Z denominator = 1);
	Rational<Z>(const Rational<Z> &r);
	
	template <typename ZZ>
	operator Rational<ZZ>();
	
	double to_double();
	//operator double(); removed due to ambigiusness with constructor and copy-constructor
	
	Rational<Z> &operator=(const Rational<Z> &r);
	
    Rational<Z> &operator+=(const Rational<Z> &r);
    Rational<Z> &operator-=(const Rational<Z> &r);
    Rational<Z> &operator*=(const Rational<Z> &r);
    Rational<Z> &operator/=(const Rational<Z> &r);
	
    Rational<Z> &operator++();	//prefix
    Rational<Z> operator++(int unused);	//postfix
    Rational<Z> &operator--();	//prefix
    Rational<Z> operator--(int unused);	//postfix
	
	void reduce();
	
	// Arithmetic friends
	template <typename Zo>
	friend Rational<Zo> operator+(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend Rational<Zo> operator-(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend Rational<Zo> operator*(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend Rational<Zo> operator/(const Rational<Zo> &a, const Rational<Zo> &b);
	
	// Relationships friends
	template <typename Zo>
	friend bool operator==(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend bool operator!=(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend bool operator<=(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend bool operator>=(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend bool operator<(const Rational<Zo> &a, const Rational<Zo> &b);
	template <typename Zo>
	friend bool operator>(const Rational<Zo> &a, const Rational<Zo> &b);
	
	// IO
	template <typename Zo>
	friend std::ostream &operator<<(std::ostream &stream, const Rational<Zo> &r);
	
protected:
	Z m_numerator;
	Z m_denominator;
};

typedef Rational<int32_t> Rational32;
typedef Rational<int16_t> Rational16;
typedef Rational<int8_t> Rational8;

template <typename Z>
Rational<Z>::Rational(Z numerator, Z denominator)
	:m_numerator(numerator), m_denominator(denominator)
{}

template <typename Z>
Rational<Z>::Rational(const Rational<Z> &r)
	:m_numerator(r.m_numerator), m_denominator(r.m_denominator)
{}

template <typename Z>
template <typename ZZ>
Rational<Z>::operator Rational<ZZ>()
{
	return Rational<ZZ>(m_numerator, m_denominator);
}

template <typename Z>
double Rational<Z>::to_double()
{
	return double(m_numerator)/m_denominator;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator=(const Rational<Z> &r)
{
	m_numerator = r.m_numerator;
	m_denominator = r.m_denominator;
	return *this;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator+=(const Rational<Z> &r)
{
	Z lcm = least_common_multiple(m_denominator, r.m_denominator);
	m_numerator = m_numerator * (lcm/m_denominator) + 
					r.m_numerator * (lcm/r.m_denominator);
	m_denominator = lcm;
	return *this;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator-=(const Rational<Z> &r)
{
	Z lcm = least_common_multiple(m_denominator, r.m_denominator);
	m_numerator = m_numerator * (lcm/m_denominator) - 
					r.m_numerator * (lcm/r.m_denominator);
	m_denominator = lcm;
	return *this;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator*=(const Rational<Z> &r)
{
	m_numerator *= r.m_numerator;
	m_denominator *= r.m_denominator;
	return *this;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator/=(const Rational<Z> &r)
{
	m_numerator *= r.m_denominator;
	m_denominator *= r.m_numerator;
	return *this;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator++()	//prefix
{
	*this += Rational<Z>(m_denominator,m_denominator);
	return *this;
}

template <typename Z>
Rational<Z> Rational<Z>::operator++(int unused)	//postfix
{
	Rational<Z> r = *this;
	++(*this);
	return r;
}

template <typename Z>
Rational<Z> &Rational<Z>::operator--()
{
	*this -= Rational<Z>(m_denominator,m_denominator);
	return *this;
}

template <typename Z>
Rational<Z> Rational<Z>::operator--(int unused)
{
	Rational<Z> r = *this;
	--(*this);
	return r;
}

template <typename Z>
void Rational<Z>::reduce()
{
	Z gcd = greatest_common_divisor(std::abs(m_numerator), std::abs(m_denominator));
	m_numerator /= gcd;
	m_denominator /= gcd;
}

template <typename Z>
Rational<Z> operator+(const Rational<Z> &a, const Rational<Z> &b)
{
	Rational<Z> r = a;
	return r += b;
}
template <typename Z>
Rational<Z> operator-(const Rational<Z> &a, const Rational<Z> &b)
{
	Rational<Z> r = a;
	return r -= b;
}
template <typename Z>
Rational<Z> operator*(const Rational<Z> &a, const Rational<Z> &b)
{
	Rational<Z> r = a;
	return r *= b;
}
template <typename Z>
Rational<Z> operator/(const Rational<Z> &a, const Rational<Z> &b)
{
	Rational<Z> r = a;
	return r /= b;
}

template <typename Z>
std::ostream &operator<<(std::ostream &stream, const Rational<Z> &r)
{
	return stream << r.m_numerator << "/" << r.m_denominator;
}

template <typename Z>
bool operator==(const Rational<Z> &a, const Rational<Z> &b)
{
	return !(a<b) && !(a>b);
}

template <typename Z>
bool operator!=(const Rational<Z> &a, const Rational<Z> &b)
{
	return !(a==b);
}

template <typename Z>
bool operator<=(const Rational<Z> &a, const Rational<Z> &b)
{
	return a<b || a==b;
}

template <typename Z>
bool operator>=(const Rational<Z> &a, const Rational<Z> &b)
{
	return a>b || a==b;
}

template <typename Z>
bool operator<(const Rational<Z> &a, const Rational<Z> &b)
{
	Z lcm = least_common_multiple(a.m_denominator, b.m_denominator);
	return a.m_numerator * (lcm/a.m_denominator) 
			< b.m_numerator * (lcm/b.m_denominator);
}

template <typename Z>
bool operator>(const Rational<Z> &a, const Rational<Z> &b)
{
	Z lcm = least_common_multiple(a.m_denominator, b.m_denominator);
	return a.m_numerator * (lcm/a.m_denominator) 
			> b.m_numerator * (lcm/b.m_denominator);
}

int main(int argc, char **argv)
{
	Rational32 r(1,2);
	Rational32 r2(1,4);
	Rational32 r1(5,5);
	
	r += r2;
	double rd = r.to_double();
	bool less = r < r2;
	Rational32 r3 = r+r2;
	Rational16 r3_16 = r+r2;
	Rational32 r3_copy(r3_16);
	Rational32 r3_16_copy(r3_16);
	
	std::cout << "rd=" << rd << std::endl;
	std::cout << "r1=" << r1 << std::endl;
	r1.reduce();
	std::cout << "r1 (reduced)=" << r1 << std::endl;
	std::cout << "r=" << r << std::endl;
	std::cout << "r2=" << r2 << std::endl;
	std::cout << "r3=" << r3 << std::endl;
	std::cout << "r3_16=" << r3_16 << std::endl;
	std::cout << "r3_copy=" << r3_copy << std::endl;
	std::cout << "r3_16_copy=" << r3_16_copy << std::endl;
	std::cout << r + r2 << std::endl;
	std::cout << less << std::endl;
	
	return 0;
}
